package com.bsj.travel.device.service.impl;

import com.alibaba.fastjson2.JSON;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.bsj.travel.cached.redis.RedisCached;
import com.bsj.travel.config.rocketmq.producer.BaseMqPushUtil;
import com.bsj.travel.constant.*;
import com.bsj.travel.constants.RedisConstant;
import com.bsj.travel.def.common.DO.ProductDO;
import com.bsj.travel.def.common.DO.ProductTopicDO;
import com.bsj.travel.def.common.DTO.web.productTopic.ProductTopicDTO;
import com.bsj.travel.def.common.DTO.web.productTopic.ProductTopicSearchDTO;
import com.bsj.travel.def.common.VO.ProductTopicVO;
import com.bsj.travel.def.global.JsonResult;
import com.bsj.travel.def.global.JsonResultEnum;
import com.bsj.travel.def.global.mq.Rp;
import com.bsj.travel.device.RemoteProductService;
import com.bsj.travel.device.cache.TopicCache;
import com.bsj.travel.device.service.ProductService;
import com.bsj.travel.enums.MqttEventEnum;
import com.bsj.travel.exception.ApiException;
import com.bsj.travel.mapper.mysql.ProductMapper;
import com.bsj.travel.mapper.mysql.ProductTopicMapper;
import com.bsj.travel.device.service.ProductTopicService;
import com.bsj.travel.security.auth.AuthProduct;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.context.config.annotation.RefreshScope;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Isolation;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;

import javax.annotation.Resource;
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;
import java.util.Set;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Collectors;

/**
 * @author yinhao
 * @version 1.0
 * @description
 * @date 2023/11/23
 */
@Service
@Slf4j
@RefreshScope
@Transactional(isolation = Isolation.DEFAULT, propagation = Propagation.REQUIRES_NEW, rollbackFor = Exception.class)
public class ProductTopicServiceImpl implements ProductTopicService {

    @Resource
    private ProductTopicMapper productTopicMapper;

    @Resource
    private ProductMapper productMapper;

    @Resource
    private AuthProduct authProduct;

    @Autowired
    private BaseMqPushUtil baseMqPushUtil;

    @Autowired
    private RedisCached redisCached;

    @Autowired
    private RemoteProductService remoteProductService;

    @Autowired
    private ProductService productService;

    /**
     * 根据产品ID查询关联主题信息
     *
     * @param productId
     * @return
     */
    @Override
    public List<ProductTopicDO> queryTopicByProductId(Long productId) {
        return productTopicMapper.queryTopicByProductId(productId);
    }

    /**
     * 获取对应产品的所有主题数据
     *
     * @param productKey
     * @return
     */
    @Override
    public List<ProductTopicDO> getAllList(String productKey) {
        Set<ProductTopicDO> productTopicDOS = TopicCache.getInstance().get(productKey);
        return new ArrayList<>(productTopicDOS);
    }

    /**
     * 添加主题
     *
     * @param productTopicDTO
     * @return com.bsj.travel.def.global.JsonResult
     * @author ljx
     * @time 2024/1/19 11:09
     */
    @Override
    public JsonResult addProductTopic(ProductTopicDTO productTopicDTO) {
        Long productId = productTopicDTO.getProductId();
        authProduct.permissionCheck(productId);
        if (productId == 0L) {
            return JsonResult.fail(JsonResultEnum.PUBLIC_TOPIC_CANNOT_BE_ADD);
        }
        ProductDO productDO = productMapper.selectById(productId);
        if (productDO == null) {
            return JsonResult.fail(JsonResultEnum.PRODUCT_IS_NOT_EXIST);
        }
        String productKey = productDO.getProductKey();
        paramCheck(productTopicDTO);
        ProductTopicDO productTopicDO = new ProductTopicDO();
        BeanUtils.copyProperties(productTopicDTO, productTopicDO);
        productTopicDO.setType(NumericalConstants.THREE);
        productTopicDO.setEvent(MqttEventEnum.CUSTOMIZE.name());
        productTopicDO.setProductKey(productKey);
        if (productTopicMapper.insert(productTopicDO) > 0) {
            redisCached.hset(RedisConstant.PRODUCT_CUSTOMIZE_TOPIC_KEY + productKey, String.valueOf(productTopicDO.getTopicId()), JSON.toJSONString(productTopicDO));
            //mq同步给集群
            Rp<ProductTopicDO> rp = new Rp<>();
            rp.setTag(BaseServerTopicConstants.BASE_CHANGE_TOPIC_UPDATE_TAG);
            rp.setData(productTopicDO);
            rp.setProductKey(productTopicDO.getProductKey());
            baseMqPushUtil.sendTopicBytes(BaseServerTopicConstants.BASE_SERVER_CHANGE_TOPIC, BaseServerTopicConstants.BASE_CHANGE_TOPIC_UPDATE_TAG, JSON.toJSONBytes(rp));
            return JsonResult.success();
        } else {
            return JsonResult.fail();
        }
    }

    /**
     * Topic类校验
     *
     * @param productTopicDTO
     * @return void
     * @author ljx
     * @time 2024/2/26 16:24
     */
    private void paramCheck(ProductTopicDTO productTopicDTO) {
        Long topicId = productTopicDTO.getTopicId();
        String topicClass = productTopicDTO.getTopicClass();
        int length = topicClass.length();
        //只能以/开头
        if (topicClass.charAt(NumericalConstants.ZERO) != Constants.SLASH) {
            throw new ApiException(JsonResultEnum.TOPIC_CLASS_FORMAT_ERROR);
        }
        //不能以/结尾
        if (topicClass.charAt(length - NumericalConstants.ONE) == Constants.SLASH) {
            throw new ApiException(JsonResultEnum.TOPIC_CLASS_FORMAT_ERROR);
        }
        //不能包含中文
        Pattern pattern = Pattern.compile(RegularExpressionConstants.CHINESE);
        Matcher matcher = pattern.matcher(topicClass);
        if (matcher.find()) {
            throw new ApiException(JsonResultEnum.TOPIC_CLASS_FORMAT_ERROR);
        }
        //不能包含符号但/和_除外
        if (!topicClass.matches(RegularExpressionConstants.TOPICS_REGEX)) {
            throw new ApiException(JsonResultEnum.TOPIC_CLASS_FORMAT_ERROR);
        }
        topicClass = Constants.TOPIC_PREFIX + topicClass;
        //同一个产品内Topic类不能重复
        QueryWrapper<ProductTopicDO> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("topicClass", topicClass);
        queryWrapper.eq("productId", productTopicDTO.getProductId());
        if (topicId != null) {
            queryWrapper.ne("topicId", topicId);
        }
        if (productTopicMapper.exists(queryWrapper)) {
            throw new ApiException(JsonResultEnum.TOPIC_CLASS_IS_NOT_NULL);
        }
        productTopicDTO.setTopicClass(topicClass);
    }

    /**
     * 删除主题
     *
     * @param topicId
     * @return com.bsj.travel.def.global.JsonResult
     * @author ljx
     * @time 2024/1/19 11:10
     */
    @Override
    public JsonResult deleteProductTopic(Long topicId) {
        if (topicId == null) {
            return JsonResult.fail(JsonResultEnum.PARAM_ERROR);
        }
        ProductTopicDO productTopicDO = productTopicMapper.selectById(topicId);
        if (productTopicDO == null) {
            return JsonResult.fail(JsonResultEnum.TOPIC_IS_NOT_EXIST);
        }
        Long productId = productTopicDO.getProductId();
        authProduct.permissionCheck(productId);
        if (productId == 0L) {
            return JsonResult.fail(JsonResultEnum.PUBLIC_TOPIC_CANNOT_BE_DELETE);
        }
        ProductDO productDO = productMapper.selectById(productId);
        if (productDO == null) {
            return JsonResult.fail(JsonResultEnum.PRODUCT_IS_NOT_EXIST);
        }
        if (productTopicMapper.deleteById(topicId) > 0) {
            String productKey = productDO.getProductKey();
            productTopicDO.setProductKey(productKey);
            redisCached.hdel(RedisConstant.PRODUCT_CUSTOMIZE_TOPIC_KEY + productKey, String.valueOf(topicId));
            //mq同步给集群
            Rp<ProductTopicDO> rp = new Rp<>();
            rp.setTag(BaseServerTopicConstants.BASE_CHANGE_TOPIC_DELETE_TAG);
            rp.setData(productTopicDO);
            rp.setProductKey(productTopicDO.getProductKey());
            baseMqPushUtil.sendTopicBytes(BaseServerTopicConstants.BASE_SERVER_CHANGE_TOPIC, BaseServerTopicConstants.BASE_CHANGE_TOPIC_DELETE_TAG, JSON.toJSONBytes(rp));
            return JsonResult.success();
        } else {
            return JsonResult.fail();
        }
    }

    /**
     * 修改主题
     *
     * @param productTopicDTO
     * @return com.bsj.travel.def.global.JsonResult
     * @author ljx
     * @time 2024/1/19 11:10
     */
    @Override
    public JsonResult updateProductTopic(ProductTopicDTO productTopicDTO) {
        Long topicId = productTopicDTO.getTopicId();
        if (topicId == null) {
            return JsonResult.fail(JsonResultEnum.PARAM_ERROR);
        }
        ProductTopicDO oldProductTopicDO = productTopicMapper.selectById(topicId);
        if (oldProductTopicDO == null) {
            return JsonResult.fail(JsonResultEnum.TOPIC_IS_NOT_EXIST);
        }
        //不允许改绑产品
        Long newProductId = productTopicDTO.getProductId();
        Long oldProductId = oldProductTopicDO.getProductId();
        if (!newProductId.equals(oldProductId)) {
            return JsonResult.fail(JsonResultEnum.CANNOT_CHANGE_THE_BINDING_PRODUCT);
        }
        authProduct.permissionCheck(newProductId);
        //公共主题无法修改
        if (newProductId == 0L) {
            return JsonResult.fail(JsonResultEnum.PUBLIC_TOPIC_CANNOT_BE_UPDATE);
        }
        ProductDO productDO = productMapper.selectById(newProductId);
        if (productDO == null) {
            return JsonResult.fail(JsonResultEnum.PRODUCT_IS_NOT_EXIST);
        }
        String productKey = productDO.getProductKey();
        paramCheck(productTopicDTO);
        ProductTopicDO productTopicDO = new ProductTopicDO();
        BeanUtils.copyProperties(productTopicDTO, productTopicDO);
        productTopicDO.setType(NumericalConstants.THREE);
        productTopicDO.setEvent(MqttEventEnum.CUSTOMIZE.name());
        productTopicDO.setProductKey(productKey);
        if (productTopicMapper.updateById(productTopicDO) > 0) {
            redisCached.hset(RedisConstant.PRODUCT_CUSTOMIZE_TOPIC_KEY + productKey, String.valueOf(topicId), JSON.toJSONString(productTopicDO));
            //mq同步给集群
            Rp<ProductTopicDO> rp = new Rp<>();
            rp.setTag(BaseServerTopicConstants.BASE_CHANGE_TOPIC_UPDATE_TAG);
            rp.setData(productTopicDO);
            rp.setProductKey(productTopicDO.getProductKey());
            baseMqPushUtil.sendTopicBytes(BaseServerTopicConstants.BASE_SERVER_CHANGE_TOPIC, BaseServerTopicConstants.BASE_CHANGE_TOPIC_UPDATE_TAG, JSON.toJSONBytes(rp));
            return JsonResult.success();
        } else {
            return JsonResult.fail();
        }
    }

    /**
     * 根据主题id获取主题信息
     *
     * @param topicId
     * @return com.bsj.travel.def.common.VO.ProductTopicVO
     * @author ljx
     * @time 2024/1/19 11:10
     */
    @Override
    public ProductTopicVO getProductTopicInfoById(Long topicId) {
        if (topicId == null) {
            throw new ApiException(JsonResultEnum.PARAM_ERROR);
        }
        ProductTopicDO productTopicDO = productTopicMapper.selectById(topicId);
        if (productTopicDO == null) {
            throw new ApiException(JsonResultEnum.TOPIC_IS_NOT_EXIST);
        }
        ProductTopicVO productTopicVO = new ProductTopicVO();
        BeanUtils.copyProperties(productTopicDO, productTopicVO);
        return productTopicVO;
    }

    /**
     * 主题列表
     *
     * @param productTopicSearchDTO
     * @return java.util.List < com.bsj.travel.def.common.VO.ProductTopicVO>
     * @author ljx
     * @time 2024/1/19 11:11
     */
    @Override
    public List<ProductTopicVO> listProductTopic(ProductTopicSearchDTO productTopicSearchDTO) {
        QueryWrapper<ProductTopicDO> queryWrapper = new QueryWrapper<>();
        Integer purview = productTopicSearchDTO.getPurview();
        if (purview != null) {
            if (!purview.equals(NumericalConstants.ONE) && !purview.equals(NumericalConstants.TWO) && !purview.equals(NumericalConstants.THREE)) {
                throw new ApiException(JsonResultEnum.PARAM_ERROR);
            }
            List<Integer> purviewList = new ArrayList<>();
            purviewList.add(purview);
            if (purview.equals(NumericalConstants.ONE) || purview.equals(NumericalConstants.TWO)) {
                purviewList.add(NumericalConstants.THREE);
            }
            queryWrapper.in("purview", purviewList);
        }

        List<Long> productIds = new LinkedList<>();
        Long productId = productTopicSearchDTO.getProductId();
        //如果为空则查当前用户下所有产品的Topic以及公共Topic,否则只查该产品以及公共Topic
        if (productId == null) {
            List<ProductDO> productDOS = productService.selectProductList(NumericalConstants.TWO);
            if (!CollectionUtils.isEmpty(productDOS)) {
                productIds.addAll(productDOS.stream().map(ProductDO::getProductId).collect(Collectors.toList()));
            }
        } else {
            if (!productMapper.exists(Wrappers.<ProductDO>lambdaQuery().eq(ProductDO::getProductId, productId))) {
                log.info("产品不存在,产品id{}", productId);
                throw new ApiException(JsonResultEnum.PRODUCT_IS_NOT_EXIST);
            }
            authProduct.permissionCheck(productId);
            productIds.add(productId);
        }
        productIds.add((long) NumericalConstants.ZERO);
        queryWrapper.in("productId", productIds);
        List<ProductTopicDO> productTopicDOS = productTopicMapper.selectList(queryWrapper);
        List<ProductTopicVO> productTopicVOS = productTopicDOS.stream().map(productTopicDO -> {
            ProductTopicVO productTopicVO = new ProductTopicVO();
            BeanUtils.copyProperties(productTopicDO, productTopicVO);
            return productTopicVO;
        }).collect(Collectors.toList());
        return productTopicVOS;
    }

}
